import { logger, MCPTool } from "mcp-framework";
import { z } from "zod";
import { Browser, Frame, Page } from 'puppeteer';
import puppeteer from 'puppeteer';
import fs from 'fs-extra';
import path from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);



interface GetDsWebDocInput {
    type: 'dingding'
    url: string;
}

class GetDsWebDocTool extends MCPTool<GetDsWebDocInput> {
    name = "get_ds_web_doc_tool";
    description = `
    当用户需要查看一个网页形式的文档时，就调用getDsWebDoc工具进行文档读取
    当包含https://alidocs.dingtalk.com的域名时，工具的type参数就为dingding;
    其他域名getDsWebDoc工具暂时不支持
    `;

    schema = {
        type: {
            type: z.string(),
            description: "调用工具的文档类型",
        },
        url: {
            type: z.string(),
            description: "文档的url",
        },
    };

    async execute(input: GetDsWebDocInput) {
        const { type, url } = input;
        logger.info(`接受到问题: ${url}`);
        const startTime = Date.now();
        if (type === 'dingding') {
            const result = await getDingTalkDocContent(url);
            return result;
        }
        const endTime = Date.now();
        logger.info(`总执行时间: ${(endTime - startTime) / 1000}秒`);
        return ''
    }
}

export default GetDsWebDocTool;




/*************** 工具函数 ***************/

function delay(time: number) {
    return new Promise(function (resolve) {
      setTimeout(resolve, time)
    });
  }
  
  export class DingTalkBrowser {
  
    static argv: any = {}
  
    static browser: Browser | null = null;
    static currentPage: Page | null = null;
  
    constructor() {
    }
  
  
    static getCachePath() {
      if (this.argv.cachepath) {
        return path.join(this.argv.cachepath, 'download_md');
      }
      return path.join(__dirname, '..', 'download_md');
    }
  
    static getCookieFilePath() {
      return path.join(this.getCachePath(), 'cookie.json');
    }
  
    static writerCookie(cookieString: any[]) {
      fs.ensureFileSync(this.getCookieFilePath());
      fs.writeFileSync(this.getCookieFilePath(), JSON.stringify(cookieString));
    }
  
    static readCookie() {
      if (fs.existsSync(this.getCookieFilePath())) {
        return JSON.parse(fs.readFileSync(this.getCookieFilePath(), 'utf-8').toString());
      }
      return [];
    }
  
    static async getBrowser(): Promise<Browser> {
      logger.info('启动浏览器...');
      this.browser = await puppeteer.launch({
        headless: !this.argv.showbrowser,
        defaultViewport: null,
        args: [
          '--no-sandbox',
          '--disable-setuid-sandbox',
          '--disable-dev-shm-usage',
          '--disable-accelerated-2d-canvas',
          '--disable-gpu',
          '--window-size=1920,1080'
        ],
      });
      return this.browser;
    }
  
    static async getPage(): Promise<Page> {
      this.currentPage = await this.browser!.newPage();
      return this.currentPage;
    }
  }
  
  
  
  async function waitForIframe(page: Page, maxAttempts = 10, interval = 10000) {
    for (let i = 0; i < maxAttempts; i++) {
      const iframes = await page.frames();
      const editorFrame = iframes.find((frame: Frame) =>
        frame.url().includes('.dingtalk.com/note/edit') ||
        frame.url().includes('.dingtalk.com/note/preview'));
      const frameUrls = iframes.map((frame: Frame) => frame.url());
      logger.info(`iframe列表: ${frameUrls.join(';')}`);
      if (!editorFrame) {
        await delay(interval);
        continue;
      }
      try {
        // 等待iframe内部的某个关键元素加载完成
        await editorFrame.waitForSelector('[data-testid="doc-header-more-button"]', { timeout: 5000 });
        return editorFrame;
      } catch (e) {
        logger.info(`Iframe内容未完全加载，重试中...(${i + 1}/${maxAttempts})`);
      }
    }
    throw new Error('等待iframe加载超时');
  }
  
  
  
  export async function getDingTalkDocContent(docUrl: string) {
    try {
      let cookieString = DingTalkBrowser.readCookie();
      logger.info('启动浏览器...');
      await DingTalkBrowser.getBrowser();
      const page = await DingTalkBrowser.getPage();
  
      // 设置用户代理
      await page.setUserAgent('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36');
  
      // 设置cookie
      await page.setCookie(...cookieString);
  
      const docId = docUrl.split('/').pop()?.split('?')[0];
      if (!docId) {
        return '获取文档ID失败'
      }
      logger.info(`文档ID: ${docId}：开始访问文档页面...`);
  
  
      await page.goto(docUrl, {
        waitUntil: 'domcontentloaded',
        timeout: 30000
      });
  
      // 检查页面状态
      const currentUrl = page.url();
      if (currentUrl.includes('login.dingtalk.com')) {
        logger.warn('页面重定向到登录页面, Cookie已经过期')
        const imgElementPath = '.module-qrcode-area .base-comp-avatar-pic';
        const textElementPath = '.module-qrcode-area .base-comp-avatar-text';
        // 等待更多按钮出现并点击
        await Promise.race([
          page.waitForSelector(imgElementPath, { visible: true, timeout: 10000 }),
          page.waitForSelector(textElementPath, { visible: true, timeout: 10000 })
        ]);
        logger.info('点击登录认证')
        // 根据实际出现的元素进行点击
        const picElement = await page.$(imgElementPath);
        const textElement = await page.$(textElementPath);
        if (picElement) {
          await picElement.click();
        } else if (textElement) {
          await textElement.click();
        }
  
        // 等待登录成功
        try {
          // 等待登录页面消失或文档页面加载完成
          await Promise.race([
            page.waitForNavigation({ timeout: 60000 }), // 等待页面导航完成
            page.waitForFunction(() => {
              const picElement = document.querySelector(imgElementPath);
              const textElement = document.querySelector(textElementPath);
              return !picElement && !textElement;
            }, { timeout: 60000 }) // 等待登录按钮消失
          ]);
  
          logger.info('登录成功，继续执行...');
        } catch (error) {
          logger.error(`登录过程出错: ${error}`);
          throw error;
        }
      }
  
      await delay(1000)
  
  
      cookieString = await page.cookies();
      DingTalkBrowser.writerCookie(cookieString);
      // 在页面中执行请求
      const dentryInfo = await page.evaluate(async (docId) => {
        try {
          const response = await fetch(`https://alidocs.dingtalk.com/box/api/v2/dentry/info?dentryUuid=${docId}`, {
            method: 'GET',
            headers: {
              'Accept': 'application/json',
              'Content-Type': 'application/json'
            },
            credentials: 'include'
          });
          const data = await response.json();
          return data.data;
        } catch (error) {
          logger.error(`获取文档信息失败: ${error}`)
          return error
        }
      }, docId);
  
      // 设置下载行为
      const downloadPath = path.join(DingTalkBrowser.getCachePath(), `${docId}`); // 获取当前工作目录
  
      const fileName = dentryInfo.name.split('.adoc')[0]
      const filePpdateTime = dentryInfo.contentUpdatedTime
      logger.info(`文件名: ${fileName} 文件更新时间: ${filePpdateTime}`)
      // 判断downloadPath下是否存在 fileName.md 文件
      const filePath = path.join(downloadPath, `${fileName}-${filePpdateTime}.md`);
      if (fs.existsSync(filePath)) {
        logger.info('文件已存在, 直接返回')
        return fs.readFileSync(filePath, 'utf-8').toString();
      }
  
      const editorFrame = await waitForIframe(page);
      if (!editorFrame) {
        logger.error('获取编辑器失败')
        return '获取编辑器失败'
      }
  
  
      const client = await page.createCDPSession();
      await client.send('Page.setDownloadBehavior', {
        behavior: 'allow',
        downloadPath: downloadPath,
      });
  
      // 等待更多按钮出现并点击
      await editorFrame.waitForSelector('[data-testid="doc-header-more-button"]', { visible: true, timeout: 30000 });
      logger.info('点击更多按钮')
      await editorFrame.click('[data-testid="doc-header-more-button"]');
      await delay(1000)
  
      // 等待下载按钮出现
      await editorFrame.waitForSelector('[data-role="operationBar_export"][data-item-key="export"]', { visible: true, timeout: 30000 });
      logger.info('点击下载按钮')
      await editorFrame.click('[data-role="operationBar_export"][data-item-key="export"]');
      await delay(1000)
  
      // 等待Markdown选项出现并点击
      await editorFrame.waitForSelector('[data-role="operationBar__export_exportAsMd"][data-item-key="exportAsMd"]', { visible: true, timeout: 30000 });
      logger.info('点击Markdown选项')
      // 清空下载文件夹
      if (fs.existsSync(downloadPath)) {
        logger.info('清空下载文件夹...');
        const files = fs.readdirSync(downloadPath);
        for (const file of files) {
          fs.unlinkSync(path.join(downloadPath, file));
        }
      } else {
        fs.mkdirSync(downloadPath, { recursive: true });
      }
      await editorFrame.click('[data-role="operationBar__export_exportAsMd"][data-item-key="exportAsMd"]');
      const newFilePath = path.join(downloadPath, `${fileName}.md`);
      while (true) {
        if (fs.existsSync(newFilePath)) {
          break;
        }
        await delay(1000);
      }
  
      const content = fs.readFileSync(newFilePath, 'utf-8').toString();
      fs.writeFileSync(filePath, content);
      fs.unlinkSync(newFilePath);
  
  
      return content;
  
    } catch (error) {
      logger.error(`发生错误: ${error}`);
      return `发生错误, 请联系开发人员进行场景处理
      日志路径：${process.cwd()}/logs
      缓存路径：${DingTalkBrowser.getCachePath()}
      错误信息: ${error},
      版本：${DingTalkBrowser.argv.version}`
    } finally {
      if (DingTalkBrowser.currentPage) {
        await DingTalkBrowser.currentPage.close();
        await DingTalkBrowser.browser!.close();
        logger.info('页面已关闭');
      }
    }
  }
  